#include <linux/module.h>
#include <linux/init.h>
#include <linux/input.h>
#include <linux/device.h>
#include <linux/gpio.h>
#include <linux/of.h>
#include <linux/of_gpio.h>
#include <linux/delay.h>
#include <linux/cdev.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/platform_device.h>
#include <linux/gpio/consumer.h>

#define CONFIG_GPIOLIB 1
#define dev_name "my_ds18b20"

struct ds18b20 {
    struct input_dev *input_dev;
    struct device *device;
    struct gpio_desc *des;
    int gpio;
    int value;
    struct device_node *node;
    int major;
    int minor;
    dev_t dev;
    struct cdev cdev;
    int MY_DEV_CNT;
    char *MY_DEV_NAME;
    struct class *clazz;
    struct platform_driver * plat_driver;
    struct platform_device * plat_device;
    struct gpio_desc *gpiodesc;
   
};
static struct ds18b20 ds18b20_dev;

int DS18B20_init(void){
    int ret = 0;
    int presence = 1;
    // 拉低总线至少 480us
    gpiod_direction_output(ds18b20_dev.gpiodesc, 1);
    gpiod_set_value(ds18b20_dev.gpiodesc, 1);
    udelay(1000);
    gpiod_set_value(ds18b20_dev.gpiodesc, 0);
    udelay(500);
    // 释放总线
     gpiod_set_value(ds18b20_dev.gpiodesc, 1);
    udelay(50);
    gpiod_direction_input(ds18b20_dev.gpiodesc);
    // 等待 DS18B20 响应信号
    udelay(50);
    // 读取 DS18B20 的响应信号
    while(gpiod_get_value(ds18b20_dev.gpiodesc)){
        printk("ds18b20: 主机释放总线,交由从机\n");
        udelay(20);
        ret++;
        if (ret > 100){
           printk("ds18b20: 主机释放总线,交由从机 超时\n");
            return 0;
        }
    }
    ret = 0;
    // printk("ds18b20: 从机拉低总线，跳出循环！ \n");
    while(!gpiod_get_value(ds18b20_dev.gpiodesc)){
        // printk("ds18b20: 从机拉低总线等待读取\n");
        udelay(20);
        ret++;
        if ( ret > 100){
           printk("ds18b20: 从机拉低总线等待读取超时\n");
            return 0;
        }
    }
    // printk("ds18b20: 从机释放总线\n");
    // printk("ds18b20: DS18B20_init success\n");
    return presence;
}
// 向 DS18B20 写入一个字节
static void ds18b20_write_byte(u8 byte)
{
    int i;
    for (i = 0; i < 8; i++) {
        // 拉低总线
        gpiod_direction_output(ds18b20_dev.gpiodesc, 0);
        gpiod_set_value(ds18b20_dev.gpiodesc,0);
        udelay(2);
        // 写入位数据
        // gpiod_direction_output(ds18b20_dev.gpiodesc, (byte & 0x01));
        gpiod_set_value(ds18b20_dev.gpiodesc,(byte & 0x01));
        udelay(60);
        // 释放总线
        gpiod_set_value(ds18b20_dev.gpiodesc, 1);
        gpiod_direction_input(ds18b20_dev.gpiodesc);
        // udelay(5);
        // gpiod_direction_input(ds18b20_dev.gpiodesc);
        udelay(2);
        byte >>= 1;
    }
}

// 从 DS18B20 读取一个字节
static u8 ds18b20_read_byte(void)
{
    u8 byte = 0;
    int i;
    for (i = 0; i < 8; i++) {
        // 拉低总线
        gpiod_direction_output(ds18b20_dev.gpiodesc, 0);
        gpiod_set_value(ds18b20_dev.gpiodesc,0);
        udelay(2);
        // 释放总线
        // gpiod_direction_output(ds18b20_dev.gpiodesc, 1);
        gpiod_set_value(ds18b20_dev.gpiodesc, 1);
        gpiod_direction_input(ds18b20_dev.gpiodesc);
        udelay(8);
        // 读取位数据
        byte >>= 1;
        if ( gpiod_get_value(ds18b20_dev.gpiodesc)){
            byte |= 0x80;
        }
        udelay(55);
    }
    return byte;
}

// 读取 DS18B20 温度值
static u16 ds18b20_read_temperature(void)
{
    u8 LSB, MSB;
    u16 temperature;
    // int temp;

    if (!DS18B20_init()) {
        printk(KERN_ERR "DS18B20 initialization failed in read temperature.\n");
        return -1;
    }else{
        // printk(KERN_ERR "DS18B20 initialization success in read temperature.\n");
    }

    // 发送跳过 ROM 指令
    ds18b20_write_byte(0xCC);
    // 发送开始转换指令
    ds18b20_write_byte(0x44);

    // 等待转换完成
    mdelay(750);

    if (!DS18B20_init()) {
        printk(KERN_ERR "DS18B20 initialization failed in read temperature after conversion.\n");
        return -1;
    }else{
        // printk(KERN_ERR "DS18B20 initialization success in read temperature after conversion.\n");
    }

    // 发送跳过 ROM 指令
    ds18b20_write_byte(0xCC);
    // 发送读取温度指令
    ds18b20_write_byte(0xBE);

    // 读取温度数据
    LSB = ds18b20_read_byte();
    MSB = ds18b20_read_byte();

    temperature = (LSB << 8) | MSB ;
    // temp = (float)temperature / 16.0;
    // temp =  temperature;
    return temperature;
}

static int ds18b20_open(struct inode *inode, struct file *filp){
    printk("ds18b20_open\n");
    return 0;
}
static int ds18b20_release(struct inode *inode, struct file *filp){
    printk("ds18b20_release\n");
    return 0;
}
static ssize_t ds18b20_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos){
    int ret;
    u16 temperature;
    temperature = ds18b20_read_temperature();
    ret = copy_to_user(buf, &temperature, sizeof(temperature));
    if (ret < 0) {
        printk("ds18b20: copy_to_user failed\n");
        return -EFAULT;
    }
    return sizeof(temperature);
}

static struct file_operations platform_ds18b20_fops = {
    .owner = THIS_MODULE,
    .open = ds18b20_open,
    .release = ds18b20_release,
    .read = ds18b20_read,
    .write = NULL,
    .unlocked_ioctl = NULL,
};
static int ds18b20_probe(struct platform_device *pdev){
    int ret = 0;
    // int temperature ;
    printk("ds18b20_probe\n");
    ds18b20_dev.device = &pdev->dev;
    ds18b20_dev.node = pdev->dev.of_node;
    ds18b20_dev.plat_device = pdev;
    ds18b20_dev.gpiodesc = gpiod_get_optional(ds18b20_dev.device,"ds18b20",GPIOD_OUT_HIGH);
    if(IS_ERR(ds18b20_dev.gpiodesc)){
        printk("ds18b20: gpiod_get_optional failed\n");
        return PTR_ERR(ds18b20_dev.gpiodesc);
    }
    printk("ds18b20: gpiod_get_optional success\n");
    // 注册字符设备
    ret = alloc_chrdev_region(&ds18b20_dev.dev, 0, 1, dev_name);
    if (ret < 0) {
        printk("ds18b20: alloc_chrdev_region failed\n");
        return ret;
    }
    printk("ds18b20: alloc_chrdev_region success\n");
    ds18b20_dev.major = MAJOR(ds18b20_dev.dev);
    ds18b20_dev.minor = MINOR(ds18b20_dev.dev);
    // 初始化 cdev
    cdev_init(&ds18b20_dev.cdev, &platform_ds18b20_fops);
    // 添加 cdev
    ret = cdev_add(&ds18b20_dev.cdev, ds18b20_dev.dev, 1);
    if (ret < 0) {
        printk("ds18b20: cdev_add failed\n");
        goto error1;
    }
    ds18b20_dev.clazz = class_create(THIS_MODULE, dev_name);
    if (IS_ERR(ds18b20_dev.clazz)) {
        printk("ds18b20: class_create failed\n");
        goto error2;
    }
    ds18b20_dev.device = device_create(ds18b20_dev.clazz, NULL, ds18b20_dev.dev, NULL, dev_name);
    if (IS_ERR(ds18b20_dev.device)) {
        printk("ds18b20: device_create failed\n");
        goto error3;
    }
    if (DS18B20_init()){
        printk("ds18b20: DS18B20_init success\n");
    }
    return ret;
error1:
    unregister_chrdev_region(ds18b20_dev.dev, 1);
    return ret;
error2:
    cdev_del(&ds18b20_dev.cdev);
    unregister_chrdev_region(ds18b20_dev.dev, 1);
    return ret;
error3:
    cdev_del(&ds18b20_dev.cdev);
    class_destroy(ds18b20_dev.clazz);
    unregister_chrdev_region(ds18b20_dev.dev, 1);
    return ret;
}
static int ds18b20_remove(struct platform_device *pdev){
    // 注销字符设备
    device_destroy(ds18b20_dev.clazz, ds18b20_dev.dev);
    // 注销类
    class_destroy(ds18b20_dev.clazz);
    // 注销 cdev
    cdev_del(&ds18b20_dev.cdev);
    // 注销设备号
    unregister_chrdev_region(ds18b20_dev.dev, 1);
    // 释放 gpio
    gpiod_put(ds18b20_dev.gpiodesc);
    printk("ds18b20_remove\n");
    return 0;
}

static const struct of_device_id ds18b20_of_match[] = {
    {.compatible = dev_name, },
    { },
};
// MODULE_DEVICE_TABLE(of, ds18b20_of_match);
static struct platform_device_id id_table[] = {
    {.name = dev_name},
    { },
};
static struct platform_driver platform_ds18b20_driver = {
    .probe = ds18b20_probe,
    .remove = ds18b20_remove,
    .id_table = id_table,
    .driver = {
        .name = dev_name,
        .owner = THIS_MODULE,
        .of_match_table = ds18b20_of_match,
    },
};

static int __init ds18b20_m_init(void){
    int ret;
    ret = platform_driver_register(&platform_ds18b20_driver);
    if (ret < 0) {
        printk("ds18b20: platform_driver_register failed\n");
    }
    ds18b20_dev.plat_driver = &platform_ds18b20_driver;
    printk("ds18b20: platform_driver_register success\n");
    return ret ;
}

static void __exit ds18b20_m_exit(void){
    // gpiod_put(ds18b20_dev.gpiodesc);
    platform_driver_unregister(ds18b20_dev.plat_driver);
    printk("ds18b20: platform_driver_unregister success\n");
}

MODULE_AUTHOR("WLZ");
MODULE_LICENSE("GPL");
module_init(ds18b20_m_init);
module_exit(ds18b20_m_exit);